/*	--- Web基础 --- 

今天我们访问网站，使用App时，都是基于Web这种【Browser/Server模式】，简称【BS架构】。

【BS架构】的特点是，【客户端】只需要【浏览器】，【应用程序的逻辑和数据】都存储在【服务器端】。【浏览器】只需要【请求服务器】，获取【Web页面】，并把【Web页面】展示给用户即可。

【Web页面】具有极强的交互性。

由于【Web页面】是用HTML编写的，而HTML具备超强的表现力，并且，服务器端升级后，客户端无需任何部署就可以使用到新的版本，因此，【BS架构】升级非常容易。


===========================================================


#	HTTP协议

在Web应用中，【浏览器】请求【一个URL】，【服务器】就把生成的【HTML网页】发送给【浏览器】，而【浏览器】和【服务器】之间的【传输协议】是【HTTP】，所以：

	· HTML是一种用来【定义网页的文本】，会HTML，就可以编写网页；

	· HTTP是在网络上【传输HTML的协议】，用于【浏览器】和【服务器】的【通信】。

【HTTP协议】是一个【基于TCP协议】之上的【请求-响应协议】，它非常简单，我们先使用【Chrome浏览器】查看【新浪首页】，然后【选择View - Developer - Inspect Elements, 或者 直接F12】 就可以看到HTML：


F12的Element界面中切换到Network，重新加载页面，可以看到浏览器发出的每一个请求和响应：


PS:	使用Chrome浏览器可以方便地调试Web应用程序。


对于Browser来说，【请求页面】的流程如下：

	1.与服务器建立【TCP连接】；
	2.发送【HTTP请求】；
	3.收取【HTTP响应】，然后把【网页】在【浏览器】中【显示】出来。

浏览器发送的HTTP请求如下：

	GET / HTTP/1.1
	Host: www.sina.com.cn
	User-Agent: Mozilla/5.0 xxx
	Accept: * /*
	Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8


其中，第一行表示使用GET请求获取路径为/的资源，并使用HTTP/1.1协议，从第二行开始，每行都是以Header: Value形式表示的HTTP头，比较常用的HTTP Header包括：

	· Host: 表示请求的主机名，因为一个服务器上可能运行着多个网站，因此，Host表示浏览器正在请求的域名；

	· User-Agent: 标识客户端本身，例如Chrome浏览器的标识类似Mozilla/5.0 ... Chrome/79，IE浏览器的标识类似Mozilla/5.0 (Windows NT ...) like Gecko；

	· Accept：表示浏览器能接收的资源类型，如text/*，image/*或者* /*表示所有；

	· Accept-Language：表示浏览器偏好的语言，服务器可以据此返回不同语言的网页；

	· Accept-Encoding：表示浏览器可以支持的压缩类型，例如gzip, deflate, br。
	
-------------------
服务器的响应如下：

	HTTP/1.1 200 OK
	Content-Type: text/html
	Content-Length: 21932
	Content-Encoding: gzip
	Cache-Control: max-age=300

	<html>...网页数据...


服务器响应的第一行总是版本号+空格+数字+空格+文本，数字表示响应代码，其中2xx表示成功，3xx表示重定向，4xx表示客户端引发的错误，5xx表示服务器端引发的错误。数字是给程序识别，文本则是给开发者调试使用的。常见的响应代码有：

· 200 OK：表示成功；
· 301 Moved Permanently：表示该URL已经永久重定向；
· 302 Found：表示该URL需要临时重定向；
· 304 Not Modified：表示该资源没有修改，客户端可以使用本地缓存的版本；
· 400 Bad Request：表示客户端发送了一个错误的请求，例如参数无效；
· 401 Unauthorized：表示客户端因为身份未验证而不允许访问该URL；
· 403 Forbidden：表示服务器因为权限问题拒绝了客户端的请求；
· 404 Not Found：表示客户端请求了一个不存在的资源；
· 500 Internal Server Error：表示服务器处理时内部出错，例如因为无法连接数据库；
· 503 Service Unavailable：表示服务器此刻暂时无法处理请求。


从第二行开始，服务器每一行均返回一个HTTP头。服务器经常返回的HTTP Header包括：

· Content-Type：表示该响应内容的类型，例如text/html，image/jpeg；
· Content-Length：表示该响应内容的长度（字节数）；
· Content-Encoding：表示该响应压缩算法，例如gzip；
· Cache-Control：指示客户端应如何缓存，例如max-age=300表示可以最多缓存300秒。



【HTTP请求】和【响应】都由【HTTP Header和HTTP Body】构成，其中【HTTP Header】每行都以\r\n结束。

如果【HTTP Header】遇到两个【连续的\r\n】，那么【后面】就是【HTTP Body】。

【浏览器】读取【HTTP Body】，并根据【Header信息】中【指示】的【Content-Type、Content-Encoding等】解压后显示【网页、图像或其他内容】。

----------

通常【浏览器】获取的【第一个资源】是【HTML网页】，在网页中，如果嵌入了【JavaScript、CSS、图片、视频等】其他资源，【浏览器】会根据【资源的URL】再次向【服务器】请求【对应的资源】。

关于【HTTP协议】的详细内容，请参考HTTP权威指南一书，或者Mozilla开发者网站。

· HTTP权威指南: https://www.amazon.cn/dp/B00M2DKYRC/
· Mozilla开发者网站: https://developer.mozilla.org/zh-CN/docs/Web/HTTP

我们在前面介绍的【HTTP编程】是以【客户端】的身份去【请求服务器资源】。

现在，我们需要以【服务器的身份】响应【客户端请求】，编写服务器程序来处理客户端请求通常就称之为【Web开发】。


==================================================


#	编写HTTP Server

我们来看一下【如何编写HTTP Server】。

一个【HTTP Server】本质上是一个【TCP服务器】，我们先用【TCP编程的多线程】实现的【服务器端框架】： */
public class Server {
	public static void main(String[] args) throws IOException {
		ServerSocket ss = new ServerSocket(8080); // 监听指定端口
		System.out.println("Sever is running...");

		for (;;) {
			Socket sock = ss.accept();
			System.out.println("Connected from " + sock.getRemoteSocketAddress());

			Thread t = new Handle(socket);
			t.start();
		}
	}
}

class Handler extends Thread {
	Socket sock;

	public Handler(Socket sock) {
		this.sock = sock;
	}

	public void run() {
		try (InputStream input = this.sock.getInputStream()) {
			try (OutputStream output = this.sock.getOutputStream()) {
				handle(input, output);
			} 
		} catch (Exception e) {
			try {
				this.sock.close();
			} catch (IOException ioe) {
				//...
			}

			System.out.println("Client disconnect.");
		}
	}

	private void handle(InputStream input, OutputStream output) throws IOException {
		var reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
		var writer = new BufferedWriter(new OutputStreamReader(output, StandardCharsets.UTF_8));

		// TODO 处理HTTP请求
	}
}


/*
只需要在【handle()方法】中，用【Reader】读取【HTTP请求】，用【Writer】发送【HTTP响应】，即可实现一个【最简单的HTTP服务器】。

编写代码如下： */
private void handle(InputStream input, OutputStream output) throws IOException {

	System.out.println("Process new http request...");

	var reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
	var writer = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8));

	// 读取HTTP请求
	boolean requestOk = false; // signal

	String first = reader.readLine(); // 读取访问

	if (first.startsWith("GET / HTTP/1.")) {
		requestOk = true;
	}

	for (;;) {
		String header = reader.readLine(); // 迭代读取header,

		if (header.isEmpty()) {
			// 直到空行\r\n的时候, HTTP Header读取完毕
			break;
		}

		System.out.println(header);
	}

	System.out.println(requestOk ? "Response OK" : "Response Error");

	if (!requestOK) {
		// 发送错误响应
		writer.write("404 Not Found\r\n");
		writer.write("Content-Length: 0\r\n");
		writer.write("\r\n");
		writer.flush();
	} else {
		// 发送【成功响应】
		String data = "<html><body><h1>Hello, world</h1></body></html>";
		int length = data.getBytes(StandardCharsets.UTF_8).length;

		writer.write("HTTP/1.0 200 OK\r\n");
		writer.write("Connection: close\r\n");
		writer.write("Content-Length: " + length + "\r\n");
		writer.write("\r\n"); // 空行标识Header和Body的分隔
		writer.write(data);
		writer.flush();
	}
}

/*
这里的【核心代码】是，先【读取HTTP请求】，这里我们只处理【GET /】的【请求】。

当读取到【空行】时，表示【已读到连续两个\r\n】，说明【请求结束】，可以【发送响应】。

【发送响应】的时候，【首先】发送响应代码【HTTP/1.0 200 OK】表示【一个成功的200响应】，使用【HTTP/1.0协议】，然后，【依次发送Header】，【发送完Header】后，再发送一个【空行标识Header结束】，紧接着发送【HTTP Body】，在浏览器输入【http://local.liaoxuefeng.com:8080/】就可以看到【响应页面】：


【HTTP】目前有【多个版本】，【1.0是早期版本】，【浏览器Browser】每次【建立TCP连接】后，只发送【一个HTTP请求】并【接收一个HTTP响应】，然后就【关闭TCP连接】。

由于【创建TCP连接】本身就需要【消耗一定的时间】，因此，【HTTP 1.1】允许【浏览器】和【服务器】在【同一个TCP连接】上【反复发送、接收多个HTTP请求和响应】，这样就大大提高了传输效率。
(HTTP 1.1 就是 TCP连接短时间不中断)


我们注意到【HTTP协议】是一个【请求-响应协议】，它总是发送【一个请求】，然后接收【一个响应】。

能不能【一次性发送多个请求】，然后再接收【多个响应呢】？
【HTTP 2.0】可以支持【浏览器】同时发出【多个请求】，但【每个请求】需要【唯一标识URI】，【服务器】可以【不按】请求的【顺序】返回【多个响应】，由【浏览器】自己把【收到的响应和请求】对应起来。

可见，【HTTP 2.0】进一步提高了传输效率，因为浏览器发出一个请求后，不必等待响应，就可以继续发下一个请求。
(HTTP 2.0 多个请求 但每个请求都有URI; 响应同样使用URI,实现了异步)

【HTTP 3.0】为了进一步提高速度，将【抛弃TCP协议】，改为使用【无需创建连接的UDP协议】，目前【HTTP 3.0】仍然处于【实验阶段】。
(HTTP 3.0 ; 协议: TCP -> UDP ; 创建连接的TCP -> 无需创建连接的UDP )


=====================================================================


#	练习

编写一个简单的【HTTP服务器】 */




/*===================================================================


#	----- Web基础 の 小结 -----


1. 使用【B/S架构】时，总是通过【HTTP协议】实现通信；

2. 【Web开发】通常是指【开发 服务器端Server 的 Web APP应用程序】。
(Web开发 = 给Server做APP)

*/